{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/CoreTheGreat/HBPU-Machine-Learning-Course/blob/main/ML_Chapter3_Classification.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lPboLx_o0UxI"
   },
   "source": [
    "# 第五章：深度学习\n",
    "湖北理工学院《机器学习》课程资料\n",
    "\n",
    "作者：李辉楚吴\n",
    "\n",
    "笔记内容概述: 前馈神经网络、全连接网络、Wi-Fi动作感知"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 处理原始Mat文件，与本实验无关"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: './Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.mat'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\site-packages\\scipy\\io\\matlab\\_mio.py:39\u001b[0m, in \u001b[0;36m_open_file\u001b[1;34m(file_like, appendmat, mode)\u001b[0m\n\u001b[0;32m     38\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m---> 39\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mfile_like\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m, \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m     40\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m     41\u001b[0m     \u001b[38;5;66;03m# Probably \"not found\"\u001b[39;00m\n",
      "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: './Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.mat'",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[3], line 50\u001b[0m\n\u001b[0;32m     46\u001b[0m     \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfilename\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m saved successfully.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m     48\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m csi, csilabel, timestamp\n\u001b[1;32m---> 50\u001b[0m _, _, _ \u001b[38;5;241m=\u001b[39m \u001b[43mmat2csi\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m./Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.mat\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m     51\u001b[0m _, _, _ \u001b[38;5;241m=\u001b[39m mat2csi(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m./Data/U1_G1_N30_L_L1_D0_20200408_2_Labeled.mat\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m     52\u001b[0m _, _, _ \u001b[38;5;241m=\u001b[39m mat2csi(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m./Data/U1_G2_N10_L_L1_D0_20200408_1_Labeled.mat\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
      "Cell \u001b[1;32mIn[3], line 19\u001b[0m, in \u001b[0;36mmat2csi\u001b[1;34m(matfile)\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m''' \u001b[39;00m\n\u001b[0;32m      9\u001b[0m \u001b[38;5;124;03mChange mat to csi\u001b[39;00m\n\u001b[0;32m     10\u001b[0m \u001b[38;5;124;03mExtract csi of first T-R link\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     15\u001b[0m \u001b[38;5;124;03mtimestamp: CSI timestamp of first T-R link\u001b[39;00m\n\u001b[0;32m     16\u001b[0m \u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[0;32m     18\u001b[0m \u001b[38;5;66;03m# Load the .mat file\u001b[39;00m\n\u001b[1;32m---> 19\u001b[0m mat_data \u001b[38;5;241m=\u001b[39m \u001b[43msio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mloadmat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmatfile\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m     21\u001b[0m \u001b[38;5;66;03m# For example, if there's a key called 'data':\u001b[39;00m\n\u001b[0;32m     22\u001b[0m raw_timestamp \u001b[38;5;241m=\u001b[39m mat_data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcsi\u001b[39m\u001b[38;5;124m'\u001b[39m][:,\u001b[38;5;241m0\u001b[39m]\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\site-packages\\scipy\\io\\matlab\\_mio.py:225\u001b[0m, in \u001b[0;36mloadmat\u001b[1;34m(file_name, mdict, appendmat, **kwargs)\u001b[0m\n\u001b[0;32m     88\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m     89\u001b[0m \u001b[38;5;124;03mLoad MATLAB file.\u001b[39;00m\n\u001b[0;32m     90\u001b[0m \n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    222\u001b[0m \u001b[38;5;124;03m    3.14159265+3.14159265j])\u001b[39;00m\n\u001b[0;32m    223\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    224\u001b[0m variable_names \u001b[38;5;241m=\u001b[39m kwargs\u001b[38;5;241m.\u001b[39mpop(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mvariable_names\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[1;32m--> 225\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m _open_file_context(file_name, appendmat) \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[0;32m    226\u001b[0m     MR, _ \u001b[38;5;241m=\u001b[39m mat_reader_factory(f, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m    227\u001b[0m     matfile_dict \u001b[38;5;241m=\u001b[39m MR\u001b[38;5;241m.\u001b[39mget_variables(variable_names)\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\contextlib.py:135\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__enter__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    133\u001b[0m \u001b[38;5;28;01mdel\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39margs, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mkwds, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc\n\u001b[0;32m    134\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 135\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgen\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    136\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m:\n\u001b[0;32m    137\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgenerator didn\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mt yield\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\site-packages\\scipy\\io\\matlab\\_mio.py:17\u001b[0m, in \u001b[0;36m_open_file_context\u001b[1;34m(file_like, appendmat, mode)\u001b[0m\n\u001b[0;32m     15\u001b[0m \u001b[38;5;129m@contextmanager\u001b[39m\n\u001b[0;32m     16\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_open_file_context\u001b[39m(file_like, appendmat, mode\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrb\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[1;32m---> 17\u001b[0m     f, opened \u001b[38;5;241m=\u001b[39m \u001b[43m_open_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile_like\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mappendmat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m     18\u001b[0m     \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m     19\u001b[0m         \u001b[38;5;28;01myield\u001b[39;00m f\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\site-packages\\scipy\\io\\matlab\\_mio.py:45\u001b[0m, in \u001b[0;36m_open_file\u001b[1;34m(file_like, appendmat, mode)\u001b[0m\n\u001b[0;32m     43\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m appendmat \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m file_like\u001b[38;5;241m.\u001b[39mendswith(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.mat\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[0;32m     44\u001b[0m         file_like \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.mat\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m---> 45\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mfile_like\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m, \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m     46\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m     47\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(\n\u001b[0;32m     48\u001b[0m         \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mReader needs file name or open file-like object\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     49\u001b[0m     ) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n",
      "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: './Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.mat'"
     ]
    }
   ],
   "source": [
    "# Read U1_G1_N10_L_L1_D0_20200408_1_Labeled.mat file\n",
    "import scipy.io as sio\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import os\n",
    "\n",
    "def mat2csi(matfile):\n",
    "    ''' \n",
    "    Change mat to csi\n",
    "    Extract csi of first T-R link\n",
    "    \n",
    "    return:\n",
    "    csi: CSI data of first T-R link\n",
    "    csilabel: CSI label of first T-R link\n",
    "    timestamp: CSI timestamp of first T-R link\n",
    "    '''\n",
    "    \n",
    "    # Load the .mat file\n",
    "    mat_data = sio.loadmat(matfile)\n",
    "    \n",
    "    # For example, if there's a key called 'data':\n",
    "    raw_timestamp = mat_data['csi'][:,0]\n",
    "    raw_csi = mat_data['csi'][:,2:32]\n",
    "    raw_csilabel = mat_data['csiLabel'].reshape(-1)\n",
    "\n",
    "    # Get indices of labels > 0\n",
    "    valid_indices = raw_csilabel >= 0\n",
    "    csi = np.abs(raw_csi[valid_indices]) # Take the absolute value of the CSI data\n",
    "    csilabel = raw_csilabel[valid_indices].astype(int) # Extract the labels\n",
    "    timestamp = raw_timestamp[valid_indices].real / 10 ** 6 # Convert the timestamp to seconds, using only the real part\n",
    "    timestamp = timestamp - timestamp[0] # Normalize the timestamp\n",
    "\n",
    "    # Change to DataFrame\n",
    "    df_combined = pd.DataFrame({\n",
    "        'timestamp': timestamp,\n",
    "        'label': csilabel,\n",
    "        **{f'Channel {i}': csi[:, i-1] for i in range(1, 31)}\n",
    "    })\n",
    "\n",
    "    # Extract filename without .mat extension\n",
    "    filename = os.path.basename(matfile).split('.')[0] + '.csv'\n",
    "    \n",
    "    # Save combined DataFrame to a single CSV file\n",
    "    df_combined.to_csv(filename, index=False)\n",
    "\n",
    "    print(f'{filename} saved successfully.')\n",
    "\n",
    "    return csi, csilabel, timestamp\n",
    "\n",
    "_, _, _ = mat2csi('./Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.mat')\n",
    "_, _, _ = mat2csi('./Data/U1_G1_N30_L_L1_D0_20200408_2_Labeled.mat')\n",
    "_, _, _ = mat2csi('./Data/U1_G2_N10_L_L1_D0_20200408_1_Labeled.mat')\n",
    "_, _, _ = mat2csi('./Data/U1_G2_N30_L_L1_D0_20200408_2_Labeled.mat')\n",
    "_, _, _ = mat2csi('./Data/U1_G3_N10_L_L1_D0_20200408_1_Labeled.mat')\n",
    "_, _, _ = mat2csi('./Data/U1_G3_N30_L_L1_D0_20200408_2_Labeled.mat')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "载入csv数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./Data/U1_G1_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 1]\n",
      "./Data/U1_G2_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 2]\n",
      "./Data/U1_G3_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 3]\n",
      "./Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 1]\n",
      "./Data/U1_G2_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 2]\n",
      "./Data/U1_G3_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 3]\n",
      "Training segments: 183\n",
      "Training Segment 1: 6414\n",
      "Training Segment 2: 3504\n",
      "Training Segment 3: 1214\n",
      "Training Segment 4: 2960\n",
      "Training Segment 5: 974\n",
      "Training Segment 6: 3024\n",
      "Training Segment 7: 814\n",
      "Training Segment 8: 3248\n",
      "Training Segment 9: 734\n",
      "Training Segment 10: 2928\n",
      "Training Segment 11: 782\n",
      "Training Segment 12: 2480\n",
      "Training Segment 13: 1102\n",
      "Training Segment 14: 2656\n",
      "Training Segment 15: 750\n",
      "Training Segment 16: 2672\n",
      "Training Segment 17: 606\n",
      "Training Segment 18: 3200\n",
      "Training Segment 19: 350\n",
      "Training Segment 20: 3392\n",
      "Training Segment 21: 878\n",
      "Training Segment 22: 3136\n",
      "Training Segment 23: 686\n",
      "Training Segment 24: 3408\n",
      "Training Segment 25: 558\n",
      "Training Segment 26: 3056\n",
      "Training Segment 27: 798\n",
      "Training Segment 28: 3472\n",
      "Training Segment 29: 670\n",
      "Training Segment 30: 3328\n",
      "Training Segment 31: 446\n",
      "Training Segment 32: 3472\n",
      "Training Segment 33: 798\n",
      "Training Segment 34: 2960\n",
      "Training Segment 35: 1438\n",
      "Training Segment 36: 3184\n",
      "Training Segment 37: 1134\n",
      "Training Segment 38: 2736\n",
      "Training Segment 39: 718\n",
      "Training Segment 40: 3200\n",
      "Training Segment 41: 942\n",
      "Training Segment 42: 3168\n",
      "Training Segment 43: 1454\n",
      "Training Segment 44: 2848\n",
      "Training Segment 45: 1342\n",
      "Training Segment 46: 3168\n",
      "Training Segment 47: 910\n",
      "Training Segment 48: 2624\n",
      "Training Segment 49: 1198\n",
      "Training Segment 50: 3024\n",
      "Training Segment 51: 974\n",
      "Training Segment 52: 3088\n",
      "Training Segment 53: 974\n",
      "Training Segment 54: 3200\n",
      "Training Segment 55: 558\n",
      "Training Segment 56: 3408\n",
      "Training Segment 57: 798\n",
      "Training Segment 58: 2928\n",
      "Training Segment 59: 1038\n",
      "Training Segment 60: 3424\n",
      "Training Segment 61: 7663\n",
      "Training Segment 62: 7870\n",
      "Training Segment 63: 3632\n",
      "Training Segment 64: 1502\n",
      "Training Segment 65: 4240\n",
      "Training Segment 66: 1102\n",
      "Training Segment 67: 3392\n",
      "Training Segment 68: 1582\n",
      "Training Segment 69: 4096\n",
      "Training Segment 70: 1822\n",
      "Training Segment 71: 3520\n",
      "Training Segment 72: 1470\n",
      "Training Segment 73: 3936\n",
      "Training Segment 74: 1118\n",
      "Training Segment 75: 4480\n",
      "Training Segment 76: 1246\n",
      "Training Segment 77: 3552\n",
      "Training Segment 78: 1646\n",
      "Training Segment 79: 3824\n",
      "Training Segment 80: 2094\n",
      "Training Segment 81: 3440\n",
      "Training Segment 82: 2270\n",
      "Training Segment 83: 4160\n",
      "Training Segment 84: 1630\n",
      "Training Segment 85: 4096\n",
      "Training Segment 86: 1630\n",
      "Training Segment 87: 4256\n",
      "Training Segment 88: 1774\n",
      "Training Segment 89: 3472\n",
      "Training Segment 90: 2270\n",
      "Training Segment 91: 3600\n",
      "Training Segment 92: 2110\n",
      "Training Segment 93: 3696\n",
      "Training Segment 94: 2190\n",
      "Training Segment 95: 3888\n",
      "Training Segment 96: 1902\n",
      "Training Segment 97: 4208\n",
      "Training Segment 98: 1886\n",
      "Training Segment 99: 4256\n",
      "Training Segment 100: 2030\n",
      "Training Segment 101: 4048\n",
      "Training Segment 102: 2478\n",
      "Training Segment 103: 4000\n",
      "Training Segment 104: 2238\n",
      "Training Segment 105: 4224\n",
      "Training Segment 106: 3022\n",
      "Training Segment 107: 4480\n",
      "Training Segment 108: 1310\n",
      "Training Segment 109: 4208\n",
      "Training Segment 110: 2174\n",
      "Training Segment 111: 4096\n",
      "Training Segment 112: 2238\n",
      "Training Segment 113: 4144\n",
      "Training Segment 114: 2766\n",
      "Training Segment 115: 4832\n",
      "Training Segment 116: 1454\n",
      "Training Segment 117: 4224\n",
      "Training Segment 118: 4526\n",
      "Training Segment 119: 4336\n",
      "Training Segment 120: 3438\n",
      "Training Segment 121: 4464\n",
      "Training Segment 122: 9807\n",
      "Training Segment 123: 17694\n",
      "Training Segment 124: 2512\n",
      "Training Segment 125: 1758\n",
      "Training Segment 126: 2192\n",
      "Training Segment 127: 1150\n",
      "Training Segment 128: 2592\n",
      "Training Segment 129: 1694\n",
      "Training Segment 130: 2176\n",
      "Training Segment 131: 1198\n",
      "Training Segment 132: 2144\n",
      "Training Segment 133: 1710\n",
      "Training Segment 134: 2032\n",
      "Training Segment 135: 2222\n",
      "Training Segment 136: 2256\n",
      "Training Segment 137: 2110\n",
      "Training Segment 138: 1760\n",
      "Training Segment 139: 2718\n",
      "Training Segment 140: 2176\n",
      "Training Segment 141: 2110\n",
      "Training Segment 142: 2112\n",
      "Training Segment 143: 2798\n",
      "Training Segment 144: 2400\n",
      "Training Segment 145: 2558\n",
      "Training Segment 146: 2208\n",
      "Training Segment 147: 2926\n",
      "Training Segment 148: 2496\n",
      "Training Segment 149: 2590\n",
      "Training Segment 150: 2448\n",
      "Training Segment 151: 2526\n",
      "Training Segment 152: 2416\n",
      "Training Segment 153: 3006\n",
      "Training Segment 154: 2320\n",
      "Training Segment 155: 3518\n",
      "Training Segment 156: 2032\n",
      "Training Segment 157: 5406\n",
      "Training Segment 158: 2240\n",
      "Training Segment 159: 5278\n",
      "Training Segment 160: 2304\n",
      "Training Segment 161: 8094\n",
      "Training Segment 162: 2192\n",
      "Training Segment 163: 22366\n",
      "Training Segment 164: 2352\n",
      "Training Segment 165: 1726\n",
      "Training Segment 166: 2768\n",
      "Training Segment 167: 2430\n",
      "Training Segment 168: 2176\n",
      "Training Segment 169: 3166\n",
      "Training Segment 170: 2208\n",
      "Training Segment 171: 3950\n",
      "Training Segment 172: 3360\n",
      "Training Segment 173: 3278\n",
      "Training Segment 174: 2672\n",
      "Training Segment 175: 2558\n",
      "Training Segment 176: 2400\n",
      "Training Segment 177: 3630\n",
      "Training Segment 178: 1712\n",
      "Training Segment 179: 3838\n",
      "Training Segment 180: 2240\n",
      "Training Segment 181: 2606\n",
      "Training Segment 182: 2416\n",
      "Training Segment 183: 8559\n",
      "Testing segments: 63\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "\n",
    "# Define training and testing files\n",
    "training_files = [\n",
    "    './Data/U1_G1_N30_L_L1_D0_20200408_2_Labeled.csv',\n",
    "    './Data/U1_G2_N30_L_L1_D0_20200408_2_Labeled.csv',\n",
    "    './Data/U1_G3_N30_L_L1_D0_20200408_2_Labeled.csv']\n",
    "\n",
    "testing_files = [\n",
    "    './Data/U1_G1_N10_L_L1_D0_20200408_1_Labeled.csv',\n",
    "    './Data/U1_G2_N10_L_L1_D0_20200408_1_Labeled.csv',\n",
    "    './Data/U1_G3_N10_L_L1_D0_20200408_1_Labeled.csv'\n",
    "]\n",
    "\n",
    "# Function to read and process CSV files\n",
    "def read_csv_file(file_path):\n",
    "    print(file_path)\n",
    "    df = pd.read_csv(file_path)\n",
    "    csi = df.iloc[:, 2:].values  # All columns except 'timestamp' and 'label'\n",
    "    label = df['label'].values # 0: static, 1: up, 2: down, 3: left, 4: right\n",
    "    timestamp = df['timestamp'].values\n",
    "    print(np.unique(label))\n",
    "    return csi, label, timestamp\n",
    "\n",
    "def segment_signals(csi, label, timestamp):\n",
    "    segments = [] # Store segments\n",
    "    segment_label = label[0] # Initialize segment label\n",
    "    segment_start = 0 # Initialize segment start index\n",
    "\n",
    "    for i in range(len(label)): # Iterate through all labels\n",
    "        if label[i] != segment_label: # If the label is different from the current segment label\n",
    "            segments.append((csi[segment_start:i-1], segment_label, timestamp[segment_start:i-1])) # Append the current segment to the segments list\n",
    "            segment_start = i # Update the segment start index\n",
    "            segment_label = label[i] # Update the segment label\n",
    "\n",
    "    segments.append((csi[segment_start:], segment_label, timestamp[segment_start:])) # Append the last segment to the segments list\n",
    "    return segments\n",
    "\n",
    "# Define training and testing segments\n",
    "training_segments = []\n",
    "testing_segments = []\n",
    "\n",
    "# Read and process training files\n",
    "for file in training_files:\n",
    "    s, y, t = read_csv_file(file)\n",
    "    training_segments.extend(segment_signals(s, y, t))\n",
    "\n",
    "# Read and process testing files\n",
    "for file in testing_files:\n",
    "    s, y, t = read_csv_file(file)\n",
    "    testing_segments.extend(segment_signals(s, y, t))\n",
    "\n",
    "# Print sizes of the training segments and testing segments\n",
    "print(f\"Training segments: {len(training_segments)}\")\n",
    "\n",
    "# Print length of all training segments\n",
    "for i, (s, y, t) in enumerate(training_segments):\n",
    "    print(f\"Training Segment {i + 1}: {len(s)}\")\n",
    "\n",
    "# Print size of the testing segments\n",
    "print(f\"Testing segments: {len(testing_segments)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据对齐：通过特征提取使得每一个训练集和测试集的样本长度相同"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from scipy.stats import kurtosis\n",
    "from scipy.stats import skew\n",
    "\n",
    "# Extract features of training segments\n",
    "def extract_features(s):\n",
    "    ''' \n",
    "    Extract features of each segment\n",
    "    features include:\n",
    "    - mean\n",
    "    - std\n",
    "    - max\n",
    "    - min\n",
    "    - median\n",
    "    - kurtosis\n",
    "    - skew\n",
    "    \n",
    "    Input:\n",
    "    s: segment (N*30) in training_segments or testing_segments\n",
    "    \n",
    "    Output:\n",
    "    x: 1-D vector (8*30)\n",
    "    '''\n",
    "    x = []\n",
    "    x.extend(np.mean(s, axis=0))\n",
    "    x.extend(np.std(s, axis=0))\n",
    "    x.extend(np.max(s, axis=0))\n",
    "    x.extend(np.min(s, axis=0))\n",
    "    x.extend(np.median(s, axis=0))\n",
    "    x.extend(kurtosis(s, axis=0))\n",
    "    x.extend(skew(s, axis=0))\n",
    "\n",
    "    return np.array(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用extract_features创建训练集和测试集\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch.utils.data import DataLoader, TensorDataset\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])\n",
    "    labels = torch.tensor([item[1] for item in batch])\n",
    "    \n",
    "    one_hot_labels = torch.zeros(labels.size(0), 4)  # 4 classes\n",
    "    one_hot_labels.scatter_(1, labels.unsqueeze(1), 1)\n",
    "    return data, one_hot_labels\n",
    "\n",
    "batch_size = 4\n",
    "\n",
    "# Build training dataset\n",
    "trX = [extract_features(s) for s, _, _ in training_segments] # Extract features of training segments\n",
    "trX = torch.tensor(trX, dtype=torch.float32) # Convert trX to tensor\n",
    "trY = [y for _, y, _ in training_segments] # Extract labels of training segments\n",
    "trY = torch.tensor(trY) # Convert trY to tensor\n",
    "\n",
    "# Build testing dataset\n",
    "teX = [extract_features(s) for s, _, _ in testing_segments] # Extract features of testing segments\n",
    "teX = torch.tensor(teX, dtype=torch.float32) # Convert teX to tensor\n",
    "teY = [y for _, y, _ in testing_segments] # Extract labels of testing segments\n",
    "teY = torch.tensor(teY) # Convert teY to tensor\n",
    "\n",
    "# Normalize trX and teX\n",
    "# Calculate mean and standard deviation from the training data\n",
    "mean = trX.mean(dim=0)\n",
    "std = trX.std(dim=0)\n",
    "\n",
    "# Normalize training data\n",
    "trX = (trX - mean) / std\n",
    "\n",
    "# Normalize testing data using training mean and std\n",
    "teX = (teX - mean) / std\n",
    "\n",
    "# Build Dataset\n",
    "trDataset = TensorDataset(trX, trY) # Create training dataset\n",
    "teDataset = TensorDataset(teX, teY) # Create testing dataset\n",
    "\n",
    "# Build loader\n",
    "trLoader = DataLoader(trDataset, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate) # Create training dataloader\n",
    "teLoader = DataLoader(teDataset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate) # Create testing dataloader"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "class FNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, num_classes):\n",
    "        super(FNN, self).__init__()\n",
    "        self.fc1 = nn.Linear(input_size, hidden_size)\n",
    "        self.relu1 = nn.ReLU()\n",
    "        self.fc2 = nn.Linear(hidden_size, hidden_size)\n",
    "        self.relu2 = nn.ReLU()\n",
    "        self.fc3 = nn.Linear(hidden_size, num_classes)\n",
    "        self.softmax = nn.Softmax(dim=1)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = self.relu1(x)\n",
    "        x = self.fc2(x)\n",
    "        x = self.relu2(x)\n",
    "        x = self.fc3(x)\n",
    "        out = self.softmax(x)\n",
    "        return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用Adam作为Optimizor训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FNN(\n",
      "  (fc1): Linear(in_features=210, out_features=10, bias=True)\n",
      "  (relu1): ReLU()\n",
      "  (fc2): Linear(in_features=10, out_features=10, bias=True)\n",
      "  (relu2): ReLU()\n",
      "  (fc3): Linear(in_features=10, out_features=4, bias=True)\n",
      "  (softmax): Softmax(dim=1)\n",
      ")\n",
      "Epoch [1/200], Train Loss: 1.3425, CV Loss: 1.2562\n",
      "Epoch [2/200], Train Loss: 1.2224, CV Loss: 1.1096\n",
      "Epoch [3/200], Train Loss: 1.1157, CV Loss: 1.0490\n",
      "Epoch [4/200], Train Loss: 1.0481, CV Loss: 1.0230\n",
      "Epoch [5/200], Train Loss: 1.0073, CV Loss: 1.0079\n",
      "Epoch [6/200], Train Loss: 0.9729, CV Loss: 1.0079\n",
      "Epoch [7/200], Train Loss: 0.9408, CV Loss: 1.0197\n",
      "Epoch [8/200], Train Loss: 0.9077, CV Loss: 1.0249\n",
      "Epoch [9/200], Train Loss: 0.8703, CV Loss: 1.0213\n",
      "Epoch [10/200], Train Loss: 0.8422, CV Loss: 1.0078\n",
      "Epoch [11/200], Train Loss: 0.8239, CV Loss: 1.0103\n",
      "Epoch [12/200], Train Loss: 0.8121, CV Loss: 1.0098\n",
      "Epoch [13/200], Train Loss: 0.8010, CV Loss: 1.0016\n",
      "Epoch [14/200], Train Loss: 0.7928, CV Loss: 0.9909\n",
      "Epoch [15/200], Train Loss: 0.7880, CV Loss: 0.9940\n",
      "Epoch [16/200], Train Loss: 0.7830, CV Loss: 0.9959\n",
      "Epoch [17/200], Train Loss: 0.7796, CV Loss: 0.9778\n",
      "Epoch [18/200], Train Loss: 0.7770, CV Loss: 0.9868\n",
      "Epoch [19/200], Train Loss: 0.7736, CV Loss: 0.9860\n",
      "Epoch [20/200], Train Loss: 0.7707, CV Loss: 0.9840\n",
      "Epoch [21/200], Train Loss: 0.7694, CV Loss: 0.9907\n",
      "Epoch [22/200], Train Loss: 0.7676, CV Loss: 0.9869\n",
      "Epoch [23/200], Train Loss: 0.7666, CV Loss: 0.9892\n",
      "Epoch [24/200], Train Loss: 0.7653, CV Loss: 0.9914\n",
      "Epoch [25/200], Train Loss: 0.7658, CV Loss: 0.9916\n",
      "Epoch [26/200], Train Loss: 0.7630, CV Loss: 0.9922\n",
      "Epoch [27/200], Train Loss: 0.7621, CV Loss: 0.9962\n",
      "Epoch [28/200], Train Loss: 0.7614, CV Loss: 0.9923\n",
      "Epoch [29/200], Train Loss: 0.7606, CV Loss: 0.9935\n",
      "Epoch [30/200], Train Loss: 0.7597, CV Loss: 0.9996\n",
      "Epoch [31/200], Train Loss: 0.7591, CV Loss: 0.9947\n",
      "Epoch [32/200], Train Loss: 0.7584, CV Loss: 1.0015\n",
      "Epoch [33/200], Train Loss: 0.7578, CV Loss: 0.9972\n",
      "Epoch [34/200], Train Loss: 0.7573, CV Loss: 1.0031\n",
      "Epoch [35/200], Train Loss: 0.7539, CV Loss: 1.0086\n",
      "Epoch [36/200], Train Loss: 0.7523, CV Loss: 1.0041\n",
      "Epoch [37/200], Train Loss: 0.7519, CV Loss: 1.0085\n",
      "Epoch [38/200], Train Loss: 0.7516, CV Loss: 1.0103\n",
      "Epoch [39/200], Train Loss: 0.7512, CV Loss: 1.0068\n",
      "Epoch [40/200], Train Loss: 0.7511, CV Loss: 1.0104\n",
      "Epoch [41/200], Train Loss: 0.7509, CV Loss: 1.0091\n",
      "Epoch [42/200], Train Loss: 0.7507, CV Loss: 1.0049\n",
      "Epoch [43/200], Train Loss: 0.7505, CV Loss: 1.0088\n",
      "Epoch [44/200], Train Loss: 0.7504, CV Loss: 1.0068\n",
      "Epoch [45/200], Train Loss: 0.7503, CV Loss: 1.0078\n",
      "Epoch [46/200], Train Loss: 0.7502, CV Loss: 1.0073\n",
      "Epoch [47/200], Train Loss: 0.7501, CV Loss: 1.0081\n",
      "Epoch [48/200], Train Loss: 0.7500, CV Loss: 1.0064\n",
      "Epoch [49/200], Train Loss: 0.7499, CV Loss: 1.0113\n",
      "Epoch [50/200], Train Loss: 0.7499, CV Loss: 1.0012\n",
      "Epoch [51/200], Train Loss: 0.7499, CV Loss: 1.0165\n",
      "Epoch [52/200], Train Loss: 0.7497, CV Loss: 1.0093\n",
      "Epoch [53/200], Train Loss: 0.7497, CV Loss: 1.0005\n",
      "Epoch [54/200], Train Loss: 0.7497, CV Loss: 1.0176\n",
      "Epoch [55/200], Train Loss: 0.7496, CV Loss: 1.0052\n",
      "Epoch [56/200], Train Loss: 0.7496, CV Loss: 1.0151\n",
      "Epoch [57/200], Train Loss: 0.7495, CV Loss: 0.9993\n",
      "Epoch [58/200], Train Loss: 0.7495, CV Loss: 1.0157\n",
      "Epoch [59/200], Train Loss: 0.7495, CV Loss: 0.9987\n",
      "Epoch [60/200], Train Loss: 0.7495, CV Loss: 1.0157\n",
      "Epoch [61/200], Train Loss: 0.7494, CV Loss: 0.9992\n",
      "Epoch [62/200], Train Loss: 0.7494, CV Loss: 1.0182\n",
      "Epoch [63/200], Train Loss: 0.7494, CV Loss: 1.0032\n",
      "Epoch [64/200], Train Loss: 0.7493, CV Loss: 1.0164\n",
      "Epoch [65/200], Train Loss: 0.7493, CV Loss: 0.9987\n",
      "Epoch [66/200], Train Loss: 0.7493, CV Loss: 1.0180\n",
      "Epoch [67/200], Train Loss: 0.7493, CV Loss: 0.9970\n",
      "Epoch [68/200], Train Loss: 0.7493, CV Loss: 1.0201\n",
      "Epoch [69/200], Train Loss: 0.7493, CV Loss: 1.0075\n",
      "Epoch [70/200], Train Loss: 0.7493, CV Loss: 1.0154\n",
      "Epoch [71/200], Train Loss: 0.7492, CV Loss: 0.9947\n",
      "Epoch [72/200], Train Loss: 0.7493, CV Loss: 1.0158\n",
      "Epoch [73/200], Train Loss: 0.7491, CV Loss: 0.9956\n",
      "Epoch [74/200], Train Loss: 0.7492, CV Loss: 1.0204\n",
      "Epoch [75/200], Train Loss: 0.7492, CV Loss: 0.9982\n",
      "Epoch [76/200], Train Loss: 0.7493, CV Loss: 1.0143\n",
      "Epoch [77/200], Train Loss: 0.7490, CV Loss: 0.9946\n",
      "Epoch [78/200], Train Loss: 0.7492, CV Loss: 1.0149\n",
      "Epoch [79/200], Train Loss: 0.7492, CV Loss: 0.9964\n",
      "Epoch [80/200], Train Loss: 0.7492, CV Loss: 1.0214\n",
      "Epoch [81/200], Train Loss: 0.7491, CV Loss: 0.9941\n",
      "Epoch [82/200], Train Loss: 0.7492, CV Loss: 1.0153\n",
      "Epoch [83/200], Train Loss: 0.7490, CV Loss: 0.9958\n",
      "Epoch [84/200], Train Loss: 0.7492, CV Loss: 1.0101\n",
      "Epoch [85/200], Train Loss: 0.7489, CV Loss: 1.0057\n",
      "Epoch [86/200], Train Loss: 0.7489, CV Loss: 1.0216\n",
      "Epoch [87/200], Train Loss: 0.7491, CV Loss: 0.9969\n",
      "Epoch [88/200], Train Loss: 0.7491, CV Loss: 1.0220\n",
      "Epoch [89/200], Train Loss: 0.7492, CV Loss: 1.0027\n",
      "Epoch [90/200], Train Loss: 0.7489, CV Loss: 1.0230\n",
      "Epoch [91/200], Train Loss: 0.7491, CV Loss: 1.0168\n",
      "Epoch [92/200], Train Loss: 0.7491, CV Loss: 1.0047\n",
      "Epoch [93/200], Train Loss: 0.7492, CV Loss: 1.0002\n",
      "Epoch [94/200], Train Loss: 0.7490, CV Loss: 1.0231\n",
      "Epoch [95/200], Train Loss: 0.7492, CV Loss: 1.0139\n",
      "Epoch [96/200], Train Loss: 0.7489, CV Loss: 0.9942\n",
      "Epoch [97/200], Train Loss: 0.7492, CV Loss: 0.9948\n",
      "Epoch [98/200], Train Loss: 0.7488, CV Loss: 1.0089\n",
      "Epoch [99/200], Train Loss: 0.7488, CV Loss: 1.0068\n",
      "Epoch [100/200], Train Loss: 0.7488, CV Loss: 1.0247\n",
      "Epoch [101/200], Train Loss: 0.7490, CV Loss: 0.9961\n",
      "Epoch [102/200], Train Loss: 0.7492, CV Loss: 1.0008\n",
      "Epoch [103/200], Train Loss: 0.7488, CV Loss: 1.0229\n",
      "Epoch [104/200], Train Loss: 0.7491, CV Loss: 0.9921\n",
      "Epoch [105/200], Train Loss: 0.7491, CV Loss: 1.0160\n",
      "Epoch [106/200], Train Loss: 0.7488, CV Loss: 0.9929\n",
      "Epoch [107/200], Train Loss: 0.7491, CV Loss: 1.0172\n",
      "Epoch [108/200], Train Loss: 0.7490, CV Loss: 0.9892\n",
      "Epoch [109/200], Train Loss: 0.7492, CV Loss: 0.9897\n",
      "Epoch [110/200], Train Loss: 0.7492, CV Loss: 0.9967\n",
      "Epoch [111/200], Train Loss: 0.7490, CV Loss: 1.0272\n",
      "Epoch [112/200], Train Loss: 0.7492, CV Loss: 1.0163\n",
      "Epoch [113/200], Train Loss: 0.7489, CV Loss: 1.0232\n",
      "Epoch [114/200], Train Loss: 0.7492, CV Loss: 1.0235\n",
      "Epoch [115/200], Train Loss: 0.7491, CV Loss: 0.9962\n",
      "Epoch [116/200], Train Loss: 0.7489, CV Loss: 1.0226\n",
      "Epoch [117/200], Train Loss: 0.7492, CV Loss: 1.0217\n",
      "Epoch [118/200], Train Loss: 0.7491, CV Loss: 1.0043\n",
      "Epoch [119/200], Train Loss: 0.7488, CV Loss: 1.0229\n",
      "Epoch [120/200], Train Loss: 0.7491, CV Loss: 1.0115\n",
      "Epoch [121/200], Train Loss: 0.7490, CV Loss: 0.9911\n",
      "Epoch [122/200], Train Loss: 0.7492, CV Loss: 0.9931\n",
      "Epoch [123/200], Train Loss: 0.7492, CV Loss: 0.9966\n",
      "Epoch [124/200], Train Loss: 0.7491, CV Loss: 1.0095\n",
      "Epoch [125/200], Train Loss: 0.7488, CV Loss: 0.9951\n",
      "Epoch [126/200], Train Loss: 0.7491, CV Loss: 0.9949\n",
      "Epoch [127/200], Train Loss: 0.7490, CV Loss: 1.0298\n",
      "Epoch [128/200], Train Loss: 0.7491, CV Loss: 1.0178\n",
      "Epoch [129/200], Train Loss: 0.7488, CV Loss: 0.9850\n",
      "Epoch [130/200], Train Loss: 0.7492, CV Loss: 0.9884\n",
      "Epoch [131/200], Train Loss: 0.7492, CV Loss: 0.9909\n",
      "Epoch [132/200], Train Loss: 0.7491, CV Loss: 1.0008\n",
      "Epoch [133/200], Train Loss: 0.7489, CV Loss: 1.0250\n",
      "Epoch [134/200], Train Loss: 0.7492, CV Loss: 1.0219\n",
      "Epoch [135/200], Train Loss: 0.7491, CV Loss: 1.0219\n",
      "Epoch [136/200], Train Loss: 0.7490, CV Loss: 0.9863\n",
      "Epoch [137/200], Train Loss: 0.7491, CV Loss: 0.9942\n",
      "Epoch [138/200], Train Loss: 0.7490, CV Loss: 1.0257\n",
      "Epoch [139/200], Train Loss: 0.7492, CV Loss: 1.0248\n",
      "Epoch [140/200], Train Loss: 0.7491, CV Loss: 1.0260\n",
      "Epoch [141/200], Train Loss: 0.7491, CV Loss: 1.0240\n",
      "Epoch [142/200], Train Loss: 0.7509, CV Loss: 1.0216\n",
      "Epoch [143/200], Train Loss: 0.7491, CV Loss: 1.0010\n",
      "Epoch [144/200], Train Loss: 0.7490, CV Loss: 0.9858\n",
      "Epoch [145/200], Train Loss: 0.7492, CV Loss: 0.9890\n",
      "Epoch [146/200], Train Loss: 0.7491, CV Loss: 1.0031\n",
      "Epoch [147/200], Train Loss: 0.7487, CV Loss: 1.0255\n",
      "Epoch [148/200], Train Loss: 0.7491, CV Loss: 1.0010\n",
      "Epoch [149/200], Train Loss: 0.7489, CV Loss: 1.0353\n",
      "Epoch [150/200], Train Loss: 0.7493, CV Loss: 1.0119\n",
      "Epoch [151/200], Train Loss: 0.7491, CV Loss: 1.0034\n",
      "Epoch [152/200], Train Loss: 0.7490, CV Loss: 0.9709\n",
      "Epoch [153/200], Train Loss: 0.7492, CV Loss: 0.9946\n",
      "Epoch [154/200], Train Loss: 0.7490, CV Loss: 1.0245\n",
      "Epoch [155/200], Train Loss: 0.7491, CV Loss: 1.0250\n",
      "Epoch [156/200], Train Loss: 0.7491, CV Loss: 1.0234\n",
      "Epoch [157/200], Train Loss: 0.7491, CV Loss: 1.0149\n",
      "Epoch [158/200], Train Loss: 0.7489, CV Loss: 0.9846\n",
      "Epoch [159/200], Train Loss: 0.7492, CV Loss: 0.9843\n",
      "Epoch [160/200], Train Loss: 0.7491, CV Loss: 0.9844\n",
      "Epoch [161/200], Train Loss: 0.7491, CV Loss: 0.9845\n",
      "Epoch [162/200], Train Loss: 0.7491, CV Loss: 0.9852\n",
      "Epoch [163/200], Train Loss: 0.7491, CV Loss: 0.9880\n",
      "Epoch [164/200], Train Loss: 0.7491, CV Loss: 1.0074\n",
      "Epoch [165/200], Train Loss: 0.7488, CV Loss: 0.9776\n",
      "Epoch [166/200], Train Loss: 0.7492, CV Loss: 0.9832\n",
      "Epoch [167/200], Train Loss: 0.7491, CV Loss: 0.9843\n",
      "Epoch [168/200], Train Loss: 0.7491, CV Loss: 0.9844\n",
      "Epoch [169/200], Train Loss: 0.7491, CV Loss: 0.9835\n",
      "Epoch [170/200], Train Loss: 0.7491, CV Loss: 0.9831\n",
      "Epoch [171/200], Train Loss: 0.7491, CV Loss: 0.9831\n",
      "Epoch [172/200], Train Loss: 0.7491, CV Loss: 0.9834\n",
      "Epoch [173/200], Train Loss: 0.7491, CV Loss: 0.9835\n",
      "Epoch [174/200], Train Loss: 0.7491, CV Loss: 0.9840\n",
      "Epoch [175/200], Train Loss: 0.7491, CV Loss: 0.9844\n",
      "Epoch [176/200], Train Loss: 0.7491, CV Loss: 0.9856\n",
      "Epoch [177/200], Train Loss: 0.7491, CV Loss: 0.9866\n",
      "Epoch [178/200], Train Loss: 0.7491, CV Loss: 0.9934\n",
      "Epoch [179/200], Train Loss: 0.7491, CV Loss: 1.0296\n",
      "Epoch [180/200], Train Loss: 0.7491, CV Loss: 0.9736\n",
      "Epoch [181/200], Train Loss: 0.7491, CV Loss: 0.9664\n",
      "Epoch [182/200], Train Loss: 0.7491, CV Loss: 0.9672\n",
      "Epoch [183/200], Train Loss: 0.7491, CV Loss: 0.9695\n",
      "Epoch [184/200], Train Loss: 0.7491, CV Loss: 0.9707\n",
      "Epoch [185/200], Train Loss: 0.7491, CV Loss: 0.9723\n",
      "Epoch [186/200], Train Loss: 0.7509, CV Loss: 0.9732\n",
      "Epoch [187/200], Train Loss: 0.7491, CV Loss: 0.9747\n",
      "Epoch [188/200], Train Loss: 0.7491, CV Loss: 0.9759\n",
      "Epoch [189/200], Train Loss: 0.7491, CV Loss: 0.9768\n",
      "Epoch [190/200], Train Loss: 0.7491, CV Loss: 0.9774\n",
      "Epoch [191/200], Train Loss: 0.7491, CV Loss: 0.9780\n",
      "Epoch [192/200], Train Loss: 0.7491, CV Loss: 0.9787\n",
      "Epoch [193/200], Train Loss: 0.7491, CV Loss: 0.9794\n",
      "Epoch [194/200], Train Loss: 0.7491, CV Loss: 0.9802\n",
      "Epoch [195/200], Train Loss: 0.7491, CV Loss: 0.9812\n",
      "Epoch [196/200], Train Loss: 0.7491, CV Loss: 0.9848\n",
      "Epoch [197/200], Train Loss: 0.7491, CV Loss: 1.0177\n",
      "Epoch [198/200], Train Loss: 0.7491, CV Loss: 1.0169\n",
      "Epoch [199/200], Train Loss: 0.7491, CV Loss: 1.0154\n",
      "Epoch [200/200], Train Loss: 0.7490, CV Loss: 1.0126\n"
     ]
    }
   ],
   "source": [
    "# Define the model parameters\n",
    "hidden_size = 10\n",
    "\n",
    "# Instantiate the model\n",
    "input_size = trX.shape[1]\n",
    "num_classes = 4 # 3 movements and static\n",
    "model = FNN(input_size, hidden_size, num_classes)\n",
    "print(model)\n",
    "\n",
    "# Define loss function and optimizer\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.Adam(model.parameters())\n",
    "\n",
    "# Lists to store losses\n",
    "train_losses = []\n",
    "te_losses = []\n",
    "\n",
    "# Number of epochs\n",
    "num_epochs = 200\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    model.train()\n",
    "    batch_losses = []\n",
    "    \n",
    "    for batch_x, batch_y in trLoader:\n",
    "        # Forward pass\n",
    "        outputs = model(batch_x)\n",
    "        loss = criterion(outputs, batch_y)\n",
    "        \n",
    "        # Backward pass and optimize\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        batch_losses.append(loss.item())\n",
    "    \n",
    "    # Calculate average training loss for this epoch\n",
    "    avg_train_loss = sum(batch_losses) / len(batch_losses)\n",
    "    train_losses.append(avg_train_loss)\n",
    "    \n",
    "    # Evaluate on cross-validation set\n",
    "    model.eval()\n",
    "    te_batch_losses = []\n",
    "    with torch.no_grad():\n",
    "        for te_x, te_y in teLoader:\n",
    "            te_outputs = model(te_x)\n",
    "            te_loss = criterion(te_outputs, te_y)\n",
    "            te_batch_losses.append(te_loss.item())\n",
    "    \n",
    "    avg_te_loss = sum(te_batch_losses) / len(te_batch_losses)\n",
    "    te_losses.append(avg_te_loss)\n",
    "    \n",
    "    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, CV Loss: {avg_te_loss:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算精度与学习曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 99.45%\n",
      "Accuracy on cross-validation set: 71.43%\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAHACAYAAACVhTgAAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAACDMElEQVR4nO3dd3gU5doG8Ht2s5ueQEhCEkhC6D0gCAIqoAjGLnZRwYK9oEeP8nnsePDosRwblqNiB1HkWFABla7SO4QWSICEUNOzdb4/np0tyaaSZDbJ/buuvbbN7r47mczOM8/7Pq+iqqoKIiIiIiIiqpJB7wYQEREREREFOgZORERERERENWDgREREREREVAMGTkRERERERDVg4ERERERERFQDBk5EREREREQ1YOBERERERERUAwZORERERERENQjSuwFNzel04tChQ4iMjISiKHo3h4iIiIiIdKKqKoqKipCUlASDofqcUqsLnA4dOoTk5GS9m0FERERERAEiJycHHTt2rHaZVhc4RUZGApCVExUVpXNriIiIiIhIL4WFhUhOTnbHCNVpdYGT1j0vKiqKgRMREREREdVqCA+LQxAREREREdWAgRMREREREVENGDgRERERERHVoNWNcSIiIiKilsPhcMBms+ndDApgJpMJRqPxlN+HgRMRERERNUvFxcU4cOAAVFXVuykUwBRFQceOHREREXFK78PAiYiIiIiaHYfDgQMHDiAsLAxxcXG1qopGrY+qqjhy5AgOHDiAbt26nVLmiYETERERETU7NpsNqqoiLi4OoaGhejeHAlhcXBz27dsHm812SoETi0MQERERUbPFTBPVpKG2EQZORERERERENWDgRERERETUjI0aNQpTpkyp9fL79u2DoijYsGFDo7WpJWLgRERERETUBBRFqfYyadKker3v3Llz8dxzz9V6+eTkZOTm5qJv3771+rzaamkBGotDEBERERE1gdzcXPft2bNn48knn0RmZqb7sYpFLmw2G0wmU43vGxMTU6d2GI1GJCQk1Ok1xIwTEREREVGTSEhIcF+io6OhKIr7fnl5Odq0aYOvvvoKo0aNQkhICD777DMcO3YM1113HTp27IiwsDD069cPX375pc/7Vuyq16lTJ/zzn//ELbfcgsjISKSkpOC9995zP18xE7R48WIoioJff/0VgwcPRlhYGIYPH+4T1AHAtGnTEB8fj8jISNx222147LHHMGDAgHqvD4vFgvvvvx/x8fEICQnBmWeeidWrV7ufP3HiBCZMmOCunNitWzd89NFHAACr1Yp7770XiYmJCAkJQadOnTB9+vR6t6U2GDjp6H8bDuL815bi+R+36d0UIiIiomZNVVWUWu26XBpyAt5HH30U999/P7Zv345x48ahvLwcgwYNwg8//IAtW7bg9ttvx4033oi//vqr2vd5+eWXMXjwYKxfvx5333037rrrLuzYsaPa1zz++ON4+eWXsWbNGgQFBeGWW25xP/f555/j+eefx7/+9S+sXbsWKSkpmDFjxil917///e/45ptv8PHHH2PdunXo2rUrxo0bh+PHjwMAnnjiCWzbtg0//fQTtm/fjhkzZiA2NhYA8Prrr+O7777DV199hczMTHz22Wfo1KnTKbWnJuyqp6Niix078oqQEhOmd1OIiIiImrUymwO9n/xFl8/e9uw4hJkb5rB6ypQpGD9+vM9jDz/8sPv2fffdh59//hlz5szB0KFDq3yfCy64AHfffTcACcZeffVVLF68GD179qzyNc8//zxGjhwJAHjsscdw4YUXory8HCEhIXjjjTdw66234uabbwYAPPnkk1iwYAGKi4vr9T1LSkowY8YMzJw5ExkZGQCA999/HwsXLsQHH3yARx55BNnZ2Rg4cCAGDx4MAD6BUXZ2Nrp164YzzzwTiqIgNTW1Xu2oC2acdBQRLP9gJVa7zi0hIiIiokCgBQkah8OB559/Hv3790e7du0QERGBBQsWIDs7u9r36d+/v/u21iUwPz+/1q9JTEwEAPdrMjMzMWTIEJ/lK96viz179sBms2HEiBHux0wmE4YMGYLt27cDAO666y7MmjULAwYMwN///nesXLnSveykSZOwYcMG9OjRA/fffz8WLFhQ77bUFjNOOgp3nZkotjh0bgkRERFR8xZqMmLbs+N0++yGEh4e7nP/5ZdfxquvvorXXnsN/fr1Q3h4OKZMmQKr1Vrt+1QsKqEoCpxOZ61fo00a6/2aihPJnkoXRe21/t5TeywjIwP79+/Hjz/+iEWLFuHcc8/FPffcg3//+9847bTTkJWVhZ9++gmLFi3C1VdfjTFjxuDrr7+ud5tqwoyTjsK1jJOFGSciIiKiU6EoCsLMQbpcKh78N6Rly5bh0ksvxQ033ID09HR07twZu3btarTPq0qPHj2watUqn8fWrFlT7/fr2rUrzGYzli9f7n7MZrNhzZo16NWrl/uxuLg4TJo0CZ999hlee+01nyIXUVFRuOaaa/D+++9j9uzZ+Oabb9zjoxoDM046imDgRERERETV6Nq1K7755husXLkSbdu2xSuvvIK8vDyf4KIp3HfffZg8eTIGDx6M4cOHY/bs2di0aRM6d+5c42srVucDgN69e+Ouu+7CI488gpiYGKSkpODFF19EaWkpbr31VgAyjmrQoEHo06cPLBYLfvjhB/f3fvXVV5GYmIgBAwbAYDBgzpw5SEhIQJs2bRr0e3tj4KSjsGBJ6xYzcCIiIiIiP5544glkZWVh3LhxCAsLw+23347LLrsMBQUFTdqOCRMmYO/evXj44YdRXl6Oq6++GpMmTaqUhfLn2muvrfRYVlYWXnjhBTidTtx4440oKirC4MGD8csvv6Bt27YAALPZjKlTp2Lfvn0IDQ3FWWedhVmzZgEAIiIi8K9//Qu7du2C0WjE6aefjvnz58NgaLwOdYrakPUTm4HCwkJER0ejoKAAUVFRurblcGE5hv7zVxgNCnY/n9GoaV4iIiKilqS8vBxZWVlIS0tDSEiI3s1plc477zwkJCTg008/1bsp1apuW6lLbMCMk460MU4OpwqL3YmQBhxYSERERETUUEpLS/HOO+9g3LhxMBqN+PLLL7Fo0SIsXLhQ76Y1GQZOOgrzCpSKLXYGTkREREQUkBRFwfz58zFt2jRYLBb06NED33zzDcaMGaN305oMAycdGQwKws1GlFgdKLHYERsRrHeTiIiIiIgqCQ0NxaJFi/Ruhq5YjlxnYcHaXE4sEEFEREREFKgYOOlMK0leauUkuEREREREgYqBk87CWZKciIiIiCjgMXDSWbiZk+ASEREREQU6Bk4607rqMXAiIiIiIgpcDJx05ikOwTFORERERESBioGTziJcY5yYcSIiIiKihjRz5ky0adNG72a0GAycdOYe42Rl4ERERETUkimKUu1l0qRJ9X7vTp064bXXXvN57JprrsHOnTtPrdG10FoCNE6Aq7NwjnEiIiIiahVyc3Pdt2fPno0nn3wSmZmZ7sdCQ0Mb9PNCQ0Mb/D1bM2acdOYpDsExTkREREQtWUJCgvsSHR0NRVF8Hlu6dCkGDRqEkJAQdO7cGc888wzsds/J9aeffhopKSkIDg5GUlIS7r//fgDAqFGjsH//fjz44IPu7BVQORP09NNPY8CAAfj000/RqVMnREdH49prr0VRUZF7maKiIkyYMAHh4eFITEzEq6++ilGjRmHKlCn1/t7Z2dm49NJLERERgaioKFx99dU4fPiw+/mNGzdi9OjRiIyMRFRUFAYNGoQ1a9YAAPbv34+LL74Ybdu2RXh4OPr06YP58+fXuy2nghknnYVxHiciIiKiU6eqgK1Un882hQGuYKW+fvnlF9xwww14/fXXcdZZZ2HPnj24/fbbAQBPPfUUvv76a7z66quYNWsW+vTpg7y8PGzcuBEAMHfuXKSnp+P222/H5MmTq/2cPXv2YN68efjhhx9w4sQJXH311XjhhRfw/PPPAwAeeughrFixAt999x3at2+PJ598EuvWrcOAAQPq9b1UVcVll12G8PBwLFmyBHa7HXfffTeuueYaLF68GAAwYcIEDBw4EDNmzIDRaMSGDRtgMpkAAPfccw+sViuWLl2K8PBwbNu2DREREfVqy6nSNXBaunQpXnrpJaxduxa5ubn49ttvcdlll1W5/PLly/Hoo49ix44dKC0tRWpqKu644w48+OCDTdfoBsZy5EREREQNwFYK/DNJn8/+v0OAOfyU3uL555/HY489hokTJwIAOnfujOeeew5///vf8dRTTyE7OxsJCQkYM2YMTCYTUlJSMGTIEABATEwMjEYjIiMjkZCQUO3nOJ1OzJw5E5GRkQCAG2+8Eb/++iuef/55FBUV4eOPP8YXX3yBc889FwDw0UcfISmp/ut10aJF2LRpE7KyspCcnAwA+PTTT9GnTx+sXr0ap59+OrKzs/HII4+gZ8+eAIBu3bq5X5+dnY0rrrgC/fr1c68XvejaVa+kpATp6el48803a7V8eHg47r33XixduhTbt2/HP/7xD/zjH//Ae++918gtbTye4hDsqkdERETUWq1duxbPPvssIiIi3JfJkycjNzcXpaWluOqqq1BWVobOnTtj8uTJ+Pbbb3268dVWp06d3EETACQmJiI/Px8AsHfvXthsNndABgDR0dHo0aNHvb/X9u3bkZyc7A6aAKB3795o06YNtm/fDkCyXLfddhvGjBmDF154AXv27HEve//992PatGkYMWIEnnrqKWzatKnebTlVumacMjIykJGRUevlBw4ciIEDB7rvd+rU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Bk5ERERERHpj4BQoTK6Mk7UEAHxKkhMRERERkb4YOAWKChknd3EIZpyIiIiIiHTHwClQeM3jBLA4BBERERFRIGHgFChMvoGTuzgEu+oREREREemOgVOg8JrHCQAiWFWPiIiIiChgMHAKFFrg5LAATgfHOBERERERBRAGToFCG+MEALZSRGpjnCwc40REREREpDcGToEiKASAIrdtZWgTJhmn4yUMnIiIiIiI9MbAKVAoitc4pxLERgQDAI4WW3RsFBERERERAQycAovXXE5a4HS8xAqnU9WxUURERERExMApkJjD5dpShHYRZgCAw6niZBm76xERERER6YmBUyCJaC/XxYdhMhrc45zYXY+IiIiISF+6Bk5Lly7FxRdfjKSkJCiKgnnz5lW7fG5uLq6//nr06NEDBoMBU6ZMaZJ2NplIT+AEAO3CJet0tIiBExERERGRnnQNnEpKSpCeno4333yzVstbLBbExcXh8ccfR3p6eiO3TgcRCXJdlAsAngIRJVa9WkRERERERACC9PzwjIwMZGRk1Hr5Tp064T//+Q8A4MMPP2ysZuknUgucJOMUG+kKnJhxIiIiIiLSla6BU1OwWCywWDyBR2FhoY6tqYEWOBXnAQBita56HONERERERKSrFl8cYvr06YiOjnZfkpOT9W5S1dxd9VyBk6ur3rFidtUjIiIiItJTiw+cpk6dioKCAvclJydH7yZVLbJC4BTJSXCJiIiIiAJBi++qFxwcjODgYL2bUTta4FR6FHDYPMUhGDgREREREemqxWecmpXQGMAgczeh+LB7Etyj7KpHRERERKQrXTNOxcXF2L17t/t+VlYWNmzYgJiYGKSkpGDq1Kk4ePAgPvnkE/cyGzZscL/2yJEj2LBhA8xmM3r37t3UzW94BoNMglt4ACg6jLiIXgAk46SqKhRF0bmBREREREStk66B05o1azB69Gj3/YceeggAMHHiRMycORO5ubnIzs72ec3AgQPdt9euXYsvvvgCqamp2LdvX5O0udFFaoFTLtrFy1xVFrsTxRY7IkNMOjeOiIiIiKh10jVwGjVqFFRVrfL5mTNnVnqsuuVbhMhEuS7OQ5g5CGFmI0qtDhwttjJwIiIiIiLSCcc4BZqI9nKtTYLrLknOAhFERERERHph4BRo3CXJcwEAsRGcBJeIiIiISG8MnAKNFjgVS8apnSvj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vrdThfskj/f3cLoyPNq2oU0p4d19VNv2gwxV//8VW+ywu3oMOJzSNU9bjyajwecEVLnNs82WWFzdTV3jXk0GA8rtDpRZpbtjuDkI0aGybrz/Fto2pp00U1UVwSYjQrRt2hVEl9scsDqc7gybwdWN0X1b8fyPO1X5e2knEKx2J8KDJTMWYa56fE3Fv5V2ksr7BJg70HJK18MSqwMlri6skSGe72j26iZYcV9Ucdfk7yRX5bZVfA+12ucDiff/nzfvrsH+1PU713W9Vlpndfy8iJAg3aduYVe9ajBwIiIiIiIioG6xAesiExERERER1YCBExERERERUQ0YOBEREREREdWAgRMREREREVENGDgRERERERHVgIETERERERFRDRg4ERERERER1YCBExERERERUQ0YOBEREREREdWAgRMREREREVENGDgRERERERHVgIETERERERFRDRg4ERERERER1YCBExERERERUQ2C9G5AU1NVFQBQWFioc0uIiIiIiEhPWkygxQjVaXWBU1FREQAgOTlZ55YQEREREVEgKCoqQnR0dLXLKGptwqsWxOl04tChQ4iMjISiKE3++YWFhUhOTkZOTg6ioqKa/PNbA67jxsX12/i4jhsX12/j4zpuXFy/jY/ruHEF0vpVVRVFRUVISkqCwVD9KKZWl3EyGAzo2LGj3s1AVFSU7htKS8d13Li4fhsf13Hj4vptfFzHjYvrt/FxHTeuQFm/NWWaNCwOQUREREREVAMGTkRERERERDVg4NTEgoOD8dRTTyE4OFjvprRYXMeNi+u38XEdNy6u38bHddy4uH4bH9dx42qu67fVFYcgIiIiIiKqK2aciIiIiIiIasDAiYiIiIiIqAYMnIiIiIiIiGrAwImIiIiIiKgGDJya2Ntvv420tDSEhIRg0KBBWLZsmd5NapamT5+O008/HZGRkYiPj8dll12GzMxMn2UmTZoERVF8LmeccYZOLW5enn766UrrLiEhwf28qqp4+umnkZSUhNDQUIwaNQpbt27VscXNT6dOnSqtY0VRcM899wDg9ltXS5cuxcUXX4ykpCQoioJ58+b5PF+bbdZiseC+++5DbGwswsPDcckll+DAgQNN+C0CW3Xr2Gaz4dFHH0W/fv0QHh6OpKQk3HTTTTh06JDPe4waNarSdn3ttdc28TcJTDVtw7XZJ3Abrl5N69jfPllRFLz00kvuZbgNV602x2bNfV/MwKkJzZ49G1OmTMHjjz+O9evX46yzzkJGRgays7P1blqzs2TJEtxzzz34888/sXDhQtjtdowdOxYlJSU+y51//vnIzc11X+bPn69Ti5ufPn36+Ky7zZs3u5978cUX8corr+DNN9/E6tWrkZCQgPPOOw9FRUU6trh5Wb16tc/6XbhwIQDgqquuci/D7bf2SkpKkJ6ejjfffNPv87XZZqdMmYJvv/0Ws2bNwvLly1FcXIyLLroIDoejqb5GQKtuHZeWlmLdunV44oknsG7dOsydOxc7d+7EJZdcUmnZyZMn+2zX7777blM0P+DVtA0DNe8TuA1Xr6Z17L1uc3Nz8eGHH0JRFFxxxRU+y3Eb9q82x2bNfl+sUpMZMmSIeuedd/o81rNnT/Wxxx7TqUUtR35+vgpAXbJkifuxiRMnqpdeeql+jWrGnnrqKTU9Pd3vc06nU01ISFBfeOEF92Pl5eVqdHS0+s477zRRC1ueBx54QO3SpYvqdDpVVeX2eyoAqN9++637fm222ZMnT6omk0mdNWuWe5mDBw+qBoNB/fnnn5us7c1FxXXsz6pVq1QA6v79+92PjRw5Un3ggQcat3EtgL/1W9M+gdtw3dRmG7700kvVc845x+cxbsO1V/HYrCXsi5lxaiJWqxVr167F2LFjfR4fO3YsVq5cqVOrWo6CggIAQExMjM/jixcvRnx8PLp3747JkycjPz9fj+Y1S7t27UJSUhLS0tJw7bXXYu/evQCArKws5OXl+WzLwcHBGDlyJLflerJarfjss89wyy23QFEU9+PcfhtGbbbZtWvXwmaz+SyTlJSEvn37cruup4KCAiiKgjZt2vg8/vnnnyM2NhZ9+vTBww8/zEx1HVS3T+A23LAOHz6MH3/8Ebfeemul57gN107FY7OWsC8O0rsBrcXRo0fhcDjQvn17n8fbt2+PvLw8nVrVMqiqioceeghnnnkm+vbt6348IyMDV111FVJTU5GVlYUnnngC55xzDtauXdvsZqpuakOHDsUnn3yC7t274/Dhw5g2bRqGDx+OrVu3urdXf9vy/v379Whuszdv3jycPHkSkyZNcj/G7bfh1GabzcvLg9lsRtu2bSstw3103ZWXl+Oxxx7D9ddfj6ioKPfjEyZMQFpaGhISErBlyxZMnToVGzdudHdVparVtE/gNtywPv74Y0RGRmL8+PE+j3Mbrh1/x2YtYV/MwKmJeZ9NBmTDqvgY1c29996LTZs2Yfny5T6PX3PNNe7bffv2xeDBg5Gamooff/yx0o6QfGVkZLhv9+vXD8OGDUOXLl3w8ccfuwcjc1tuOB988AEyMjKQlJTkfozbb8OrzzbL7brubDYbrr32WjidTrz99ts+z02ePNl9u2/fvujWrRsGDx6MdevW4bTTTmvqpjYr9d0ncBuunw8//BATJkxASEiIz+PchmunqmMzoHnvi9lVr4nExsbCaDRWipbz8/MrRd5Ue/fddx++++47/P777+jYsWO1yyYmJiI1NRW7du1qota1HOHh4ejXrx927drlrq7Hbblh7N+/H4sWLcJtt91W7XLcfuuvNttsQkICrFYrTpw4UeUyVDObzYarr74aWVlZWLhwoU+2yZ/TTjsNJpOJ23U9VNwncBtuOMuWLUNmZmaN+2WA27A/VR2btYR9MQOnJmI2mzFo0KBKqdyFCxdi+PDhOrWq+VJVFffeey/mzp2L3377DWlpaTW+5tixY8jJyUFiYmITtLBlsVgs2L59OxITE91dFLy3ZavViiVLlnBbroePPvoI8fHxuPDCC6tdjttv/dVmmx00aBBMJpPPMrm5udiyZQu361rSgqZdu3Zh0aJFaNeuXY2v2bp1K2w2G7freqi4T+A23HA++OADDBo0COnp6TUuy23Yo6ZjsxaxL9apKEWrNGvWLNVkMqkffPCBum3bNnXKlClqeHi4um/fPr2b1uzcddddanR0tLp48WI1NzfXfSktLVVVVVWLiorUv/3tb+rKlSvVrKws9ffff1eHDRumdujQQS0sLNS59YHvb3/7m7p48WJ179696p9//qledNFFamRkpHtbfeGFF9To6Gh17ty56ubNm9XrrrtOTUxM5LqtI4fDoaakpKiPPvqoz+PcfuuuqKhIXb9+vbp+/XoVgPrKK6+o69evd1d0q802e+edd6odO3ZUFy1apK5bt04955xz1PT0dNVut+v1tQJKdevYZrOpl1xyidqxY0d1w4YNPvtli8Wiqqqq7t69W33mmWfU1atXq1lZWeqPP/6o9uzZUx04cCDXsVr9+q3tPoHbcPVq2k+oqqoWFBSoYWFh6owZMyq9nttw9Wo6NlPV5r8vZuDUxN566y01NTVVNZvN6mmnneZTPptqD4Dfy0cffaSqqqqWlpaqY8eOVePi4lSTyaSmpKSoEydOVLOzs/VteDNxzTXXqImJiarJZFKTkpLU8ePHq1u3bnU/73Q61aeeekpNSEhQg4OD1bPPPlvdvHmzji1unn755RcVgJqZmenzOLffuvv999/97hMmTpyoqmrtttmysjL13nvvVWNiYtTQ0FD1oosu4jr3Ut06zsrKqnK//Pvvv6uqqqrZ2dnq2WefrcbExKhms1nt0qWLev/996vHjh3T94sFiOrWb233CdyGq1fTfkJVVfXdd99VQ0ND1ZMnT1Z6Pbfh6tV0bKaqzX9frKiqqjZSMouIiIiIiKhF4BgnIiIiIiKiGjBwIiIiIiIiqgEDJyIiIiIiohowcCIiIiIiIqoBAyciIiIiIqIaMHAiIiIiIiKqAQMnIiIiIiKiGjBwIiIiqgNFUTBv3jy9m0FERE2MgRMRETUbkyZNgqIolS7nn3++3k0jIqIWLkjvBhAREdXF+eefj48++sjnseDgYJ1aQ0RErQUzTkRE1KwEBwcjISHB59K2bVsA0o1uxowZyMjIQGhoKNLS0jBnzhyf12/evBnnnHMOQkND0a5dO9x+++0oLi72WebDDz9Enz59EBwcjMTERNx7770+zx89ehSXX345wsLC0K1bN3z33XeN+6WJiEh3DJyIiKhFeeKJJ3DFFVdg48aNuOGGG3Dddddh+/btAIDS0lKcf/75aNu2LVavXo05c+Zg0aJFPoHRjBkzcM899+D222/H5s2b8d1336Fr164+n/HMM8/g6quvxqZNm3DBBRdgwoQJOH78eJN+TyIialqKqqqq3o0gIiKqjUmTJuGzzz5DSEiIz+OPPvoonnjiCSiKgjvvvBMzZsxwP3fGGWfgtNNOw9tvv433338fjz76KHJychAeHg4AmD9/Pi6++GIcOnQI7du3R4cOHXDzzTdj2rRpftugKAr+8Y9/4LnnngMAlJSUIDIyEvPnz+dYKyKiFoxjnIiIqFkZPXq0T2AEADExMe7bw4YN83lu2LBh2LBhAwBg+/btSE9PdwdNADBixAg4nU5kZmZCURQcOnQI5557brVt6N+/v/t2eHg4IiMjkZ+fX9+vREREzQADJyIialbCw8MrdZ2riaIoAABVVd23/S0TGhpaq/czmUyVXut0OuvUJiIial44xomIiFqUP//8s9L9nj17AgB69+6NDRs2oKSkxP38ihUrYDAY0L17d0RGRqJTp0749ddfm7TNREQU+JhxIiKiZsVisSAvL8/nsaCgIMTGxgIA5syZg8GDB+PMM8/E559/jlWrVuGDDz4AAEyYMAFPPfUUJk6ciKeffhpHjhzBfffdhxtvvBHt27cHADz99NO48847ER8fj4yMDBQVFWHFihW47777mvaLEhFRQGHgREREzcrPP/+MxMREn8d69OiBHTt2AJCKd7NmzcLdd9+NhIQEfP755+jduzcAICwsDL/88gseeOABnH766QgLC8MVV1yBV155xf1eEydORHl5OV599VU8/PDDiI2NxZVXXtl0X5CIiAISq+oREVGLoSgKvv32W1x22WV6N4WIiFoYjnEiIiIiIiKqAQMnIiIiIiKiGnCMExERtRjsfU5ERI2FGSciIiIiIqIaMHAiIiIiIiKqAQMnIiIiIiKiGjBwIiIiIiIiqgEDJyIiIiIiohowcCIiIiIiIqoBAyciIiIiIqIaMHAiIiIiIiKqAQMnIiIiIiKiGvw/DXhFQ9D5fxEAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Calculate and print accuracies for training and cross-validation sets\n",
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    # Training set accuracy\n",
    "    tr_correct = 0\n",
    "    tr_total = 0\n",
    "    for images, labels in trLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        tr_total += labels.size(0)\n",
    "        tr_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    tr_accuracy = 100 * tr_correct / tr_total\n",
    "    \n",
    "    # test set accuracy\n",
    "    te_correct = 0\n",
    "    te_total = 0\n",
    "    for images, labels in teLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        te_total += labels.size(0)\n",
    "        te_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    te_accuracy = 100 * te_correct / te_total\n",
    "\n",
    "print(f'Accuracy on training set: {tr_accuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {te_accuracy:.2f}%')\n",
    "\n",
    "# Plot training and cross-validation losses\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), train_losses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), te_losses, label='Testing Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "authorship_tag": "ABX9TyO5gS9/MePw+FDiXJA07L6y",
   "include_colab_link": true,
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
